package diary.it.ftp.server.manager.utils;

import diary.it.ftp.server.manager.model.FtpDownloadParam;
import diary.it.ftp.server.manager.model.FtpUploadParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;

/**
 * @Author it
 * @Description TODO
 * @Date 2021/9/29 21:01
 * @Version 1.0
 */
@Slf4j
@Configuration
public class FtpUtils {
    /**
     * ftp客户端文件使用的字符集
     */
    @Value("${ftp.client.charset}")
    private String ftpClientCharset;
    /**
     * ftp服务器文件使用的字符集
     */
    @Value("${ftp.server.charset}")
    private String ftpServerCharset;

    @Value("${ftp.client.charset}")
    private String clientCharset;

    /**
     * FTP协议里面，规定文件名编码为iso-8859-1
     */
    @Value("${ftp.server.charset}")
    private String serverCharset;

    /**
     * 测试连接是否成功
     *
     * @return
     */
    public Boolean connect(FTPClient ftpClient, String hostname, Integer port, String username, String password) {
        boolean flag = false;
        try {
            // ftp初始化的一些参数
            ftpClient.connect(hostname, port);
            ftpClient.enterLocalPassiveMode();
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            if (ftpClient.login(username, password)) {
                flag = true;
            } else {
                disconnect(ftpClient);
                log.info("FtpUtils connect unsuccessfully in error:{}");
            }
            return flag;
        } catch (Exception e) {
            log.error("FtpUtils connect in error:{}!", e);
            return flag;
        }
    }

    /**
     * 断开ftp连接
     */
    public void disconnect(FTPClient ftpClient) {
        if (ftpClient.isConnected()) {
            try {
                ftpClient.disconnect();
                log.info("FtpUtils disconnect successfully!");
            } catch (IOException e) {
                log.error("FtpUtils disconnect unsuccessfully!");
            }
        }
    }


    /**
     * 上传
     *
     * @return
     */
    public boolean upload(FtpUploadParam param) {
        boolean flag = false;
        FTPClient ftpClient = new FTPClient();
        //1 测试连接
        if (connect(ftpClient, param.getHostname(), param.getPort(), param.getUsername(), param.getPassword())) {
            try {
                //2 检查工作目录是否存在，不存在则创建
                if (!ftpClient.changeWorkingDirectory(param.getWorkingPath())) {
                    ftpClient.makeDirectory(param.getWorkingPath());
                }
                // 将文件编码成Ftp服务器支持的编码类型（FTP协议里面，规定文件名编码为iso-8859-1，所以目录名或文件名需要转码。）
                String fileName = new String(param.getSaveName().getBytes(ftpClientCharset), ftpServerCharset);
                // 3 上传文件
                if (ftpClient.storeFile(fileName, param.getInputStream())) {
                    flag = true;
                } else {
                    log.warn("FtpUtils uploadFile unsuccessfully!!");
                }
            } catch (IOException e) {
                log.error("FtpUtils upload in error:{}", e);
            } finally {
                disconnect(ftpClient);
            }
        }
        return flag;
    }

    public boolean download(FtpDownloadParam param, String downloadFileName) {
        FTPClient ftpClient = new FTPClient();
        FileOutputStream out = null;
        //1 测试连接
        if (connect(ftpClient, param.getHostname(), param.getPort(), param.getUsername(), param.getPassword())) {
            try {
                File file;
                String localPath = param.getDownloadPath() + param.getFileName();
                out = new FileOutputStream(new File(localPath));
                //2 检查工作目录是否存在，不存在返回
//                if (!ftpClient.changeWorkingDirectory(param.getWorkingPath())) {
//                    return false;
//                }
                /*
                 * 打开FTP服务器的PASS模式(不记得FTP协议支持的模式请翻到文章第一阶段)
                 * 这个方法的意思就是每次数据连接之前，ftp client告诉ftp server开通一个端口来传输数据. 因为ftp
                 * server可能每次开启不同的端口来传输数据，但是在linux上，由于安全限制，可能某些端口没有开启，可能出现出现阻塞
                 */
                ftpClient.enterLocalPassiveMode();
                // 设置文件的传输方式
                ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
                // 将文件编码成Ftp服务器支持的编码类型（FTP协议里面，规定文件名编码为iso-8859-1，所以目录名或文件名需要转码。）
                // 缺少编码转换会导致：从FTP服务器下载下来的文件是破损的，无法被打开
                boolean b = ftpClient.retrieveFile(new String(downloadFileName
                        .getBytes(clientCharset), serverCharset), out);
                out.flush();
            } catch (IOException e) {
                log.error("FtpUtils upload in error:{}", e);
                return false;
            } finally {
                try{
                    if(Objects.nonNull(out)){
                        out.close();
                    }
                }catch (Exception e){
                    log.error("FtpUtils upload in error:{}", e);
                }
                disconnect(ftpClient);
            }
        }
        return true;
    }
}
